using System.Collections.Generic;
using UnityEngine;

namespace Gj
{
    /// <summary>
    ///     技能施放返回值
    /// </summary>
    public enum CastResult
    {
        Success,
        Casting,
        NeedError,
        TimeError,
        NumberError,
        RangeError,
        PowerError,
        TargetRelationError,
        TargetStatusError
    }

    public class CastPart : BasePart, BaseEngine.Notice
    {
        private Cast _master;
        private Notice _notice;
        private Notice _otherNotice;
        private Cast _self;
        private readonly Dictionary<Skill, bool> ingMap = new();
        private readonly Dictionary<Skill, BaseEngine> skillMap = new();

        private readonly Dictionary<Skill, float> waitTimeMap = new();

        void BaseEngine.Notice.Start(Skill skill)
        {
            SetWaitTime(skill);
            StartCast(skill);
            if (_otherNotice != null)
                _otherNotice.Start(skill);
            else
                Debug.Log("Other Notice is Empty");
            if (_notice != null)
                _notice.Start(skill);
            else
                Debug.Log("Notice is Empty");
        }

        void BaseEngine.Notice.End(Skill skill)
        {
            if (skill.castType == SkillCastType.Sustained || skill.castType == SkillCastType.ReadyAndSustained)
                SetWaitTime(skill);
            EndCast(skill);
            if (_otherNotice != null)
                _otherNotice.End(skill);
            else
                Debug.Log("Other Notice is Empty");
            if (_notice != null)
                _notice.End(skill);
            else
                Debug.Log("Notice is Empty");
        }

        void BaseEngine.Notice.Ready(Skill skill)
        {
            if (_otherNotice != null)
                _otherNotice.Ready(skill);
            else
                Debug.Log("Other Notice is Empty");
            if (_notice != null)
                _notice.Ready(skill);
            else
                Debug.Log("Notice is Empty");
        }

        void BaseEngine.Notice.Stop(Skill skill)
        {
            EndCast(skill);
            if (_otherNotice != null)
                _otherNotice.Stop(skill);
            else
                Debug.Log("Other Notice is Empty");
            if (_notice != null)
                _notice.Stop(skill);
            else
                Debug.Log("Notice is Empty");
        }

        void BaseEngine.Notice.Cancel(Skill skill)
        {
            if (skill.castType == SkillCastType.Sustained || skill.castType == SkillCastType.ReadyAndSustained)
                SetWaitTime(skill);
            EndCast(skill);
            if (_otherNotice != null)
                _otherNotice.Cancel(skill);
            else
                Debug.Log("Other Notice is Empty");
            if (_notice != null)
                _notice.Cancel(skill);
            else
                Debug.Log("Notice is Empty");
        }

        /// <summary>
        ///     设置power
        /// </summary>
        /// <param name="cast"></param>
        public void Init(Cast cast)
        {
            _self = cast;
        }

        public void SetMaster(Cast cast)
        {
            _master = cast;
        }

        /// <summary>
        ///     设置action
        /// </summary>
        /// <param name="notice"></param>
        public void SetNotice(Notice notice)
        {
            _notice = notice;
        }

        /// <summary>
        ///     设置action
        /// </summary>
        /// <param name="notice"></param>
        public void SetOther(Notice notice)
        {
            _otherNotice = notice;
        }

        public void RefreshCast(Skill skill, Life life)
        {
            if (skillMap.ContainsKey(skill))
            {
                skillMap[skill].Set(life);
                skillMap[skill].UpdateData();
            }
            else
            {
                Debug.LogError("Skill Map is Empty");
            }
        }

        public void RefreshCast(Skill skill, Life[] lifes)
        {
            if (skillMap.ContainsKey(skill))
            {
                skillMap[skill].Set(lifes);
                skillMap[skill].UpdateData();
            }
            else
            {
                Debug.LogError("Skill Map is Empty");
            }
        }

        public void RefreshCast(Skill skill, Vector3 position)
        {
            if (skillMap.ContainsKey(skill))
            {
                skillMap[skill].Set(position);
                skillMap[skill].UpdateData();
            }
            else
            {
                Debug.LogError("Skill Map is Empty");
            }
        }

        public void RefreshCast(Skill skill, Vector3[] positions)
        {
            if (skillMap.ContainsKey(skill))
            {
                skillMap[skill].Set(positions);
                skillMap[skill].UpdateData();
            }
            else
            {
                Debug.LogError("Skill Map is Empty");
            }
        }

        /// <summary>
        ///     准备施放中 确认施放
        /// </summary>
        public void ConfirmCast()
        {
            if (skillMap.Count > 0)
                foreach (var skill in skillMap.Keys)
                {
                    ConfirmCast(skill);
                    break;
                }
            else
                Debug.LogError("Skill Map is Empty");
        }

        public void ConfirmCast(Skill skill)
        {
            if (skillMap.ContainsKey(skill))
                skillMap[skill].Confirm();
            else
                Debug.LogError("Skill Map is Empty");
        }

        /// <summary>
        ///     准备施放和施放中 取消施放
        /// </summary>
        public void CancelCast()
        {
            if (skillMap.Count > 0)
                foreach (var skill in skillMap.Keys)
                {
                    CancelCast(skill);
                    break;
                }
            else
                Debug.LogError("Skill Map is Empty");
        }

        public void CancelCast(Skill skill)
        {
            if (skillMap.ContainsKey(skill))
                skillMap[skill].Cancel();
            else
                Debug.LogError("Skill Map is Empty");
        }

        /// <summary>
        ///     施放
        /// </summary>
        /// <param name="skill"></param>
        /// <param name="skillNotice"></param>
        /// <returns></returns>
        public CastResult Cast(Skill skill)
        {
            var time = DateTools.GetNowTime();
            if (skill.caseNeed != SkillCastNeed.None && skill.caseNeed != SkillCastNeed.NoneOrTarget)
                return CastResult.NeedError;
            if (!TimeCheck(skill)) return CastResult.TimeError;
            if (!PowerCheck(skill, time)) return CastResult.PowerError;
            var baseEngine = GetEngine(skill, time);
            Cast(baseEngine, time);
            SetWaitTime(skill);
            return CastResult.Success;
        }

        /// <summary>
        ///     施放指定目标技能
        /// </summary>
        /// <param name="skill"></param>
        /// <param name="target"></param>
        /// <param name="skillNotice"></param>
        /// <returns></returns>
        public CastResult Cast(Skill skill, Life target)
        {
            var time = DateTools.GetNowTime();
            if (skill.caseNeed != SkillCastNeed.Target && skill.caseNeed != SkillCastNeed.NoneOrTarget)
                return CastResult.NeedError;
            if (!TimeCheck(skill)) return CastResult.TimeError;
            if (!PowerCheck(skill, time)) return CastResult.PowerError;
            if (!UnityTools.AllowStatus(skill, target)) return CastResult.TargetStatusError;
            if (!UnityTools.AllowCast(skill, _self, target)) return CastResult.TargetRelationError;
            if (!UnityTools.AllowRange(skill, _self, target.GetPosition())) return CastResult.RangeError;
            var baseEngine = GetEngine(skill, time);
            baseEngine.Set(target);
            Cast(baseEngine, time);
            SetWaitTime(skill);
            return CastResult.Success;
        }

        /// <summary>
        ///     施放指定目标技能
        /// </summary>
        /// <param name="skill"></param>
        /// <param name="targets"></param>
        /// <param name="skillNotice"></param>
        /// <returns></returns>
        public CastResult Cast(Skill skill, Life[] targets)
        {
            var time = DateTools.GetNowTime();
            if (skill.caseNeed != SkillCastNeed.MultipleTarget) return CastResult.NeedError;
            if (targets.Length == 0 || targets.Length > skill.number) return CastResult.NumberError;
            if (!TimeCheck(skill)) return CastResult.TimeError;
            if (!PowerCheck(skill, time)) return CastResult.PowerError;
            foreach (var target in targets)
            {
                if (!UnityTools.AllowStatus(skill, target)) return CastResult.TargetStatusError;
                if (!UnityTools.AllowCast(skill, _self, target)) return CastResult.TargetRelationError;
                if (!UnityTools.AllowRange(skill, _self, target.GetPosition())) return CastResult.RangeError;
            }

            var baseEngine = GetEngine(skill, time);
            baseEngine.Set(targets);
            Cast(baseEngine, time);
            SetWaitTime(skill);
            return CastResult.Success;
        }

        /// <summary>
        ///     施放指定点技能
        /// </summary>
        /// <param name="skill"></param>
        /// <param name="position"></param>
        /// <param name="skillNotice"></param>
        /// <returns></returns>
        public CastResult Cast(Skill skill, Vector3 position)
        {
            var time = DateTools.GetNowTime();
            if (skill.caseNeed != SkillCastNeed.Position) return CastResult.NeedError;
            if (!TimeCheck(skill)) return CastResult.TimeError;
            if (!PowerCheck(skill, time)) return CastResult.PowerError;
            if (!UnityTools.AllowRange(skill, _self, position)) return CastResult.RangeError;
            var baseEngine = GetEngine(skill, time);
            baseEngine.Set(position);
            Cast(baseEngine, time);
            SetWaitTime(skill);
            return CastResult.Success;
        }

        /// <summary>
        ///     施放指定点技能
        /// </summary>
        /// <param name="skill"></param>
        /// <param name="positions"></param>
        /// <param name="skillNotice"></param>
        /// <returns></returns>
        public CastResult Cast(Skill skill, Vector3[] positions)
        {
            var time = DateTools.GetNowTime();
            if (skill.caseNeed != SkillCastNeed.MultiplePosition) return CastResult.NeedError;
            if (positions.Length == 0 || positions.Length > skill.number) return CastResult.NumberError;
            if (!TimeCheck(skill)) return CastResult.TimeError;
            if (!PowerCheck(skill, time)) return CastResult.PowerError;
            foreach (var position in positions)
                if (!UnityTools.AllowRange(skill, _self, position))
                    return CastResult.RangeError;
            var baseEngine = GetEngine(skill, time);
            baseEngine.Set(positions);
            Cast(baseEngine, time);
            SetWaitTime(skill);
            return CastResult.Success;
        }

        /// <summary>
        ///     获取技能引擎
        /// </summary>
        /// <param name="skill"></param>
        /// <returns></returns>
        private BaseEngine GetEngine(Skill skill, int time)
        {
            return EngineService.single.MakeEngine(_master != null ? _master : _self, skill, time);
        }

        private string GetKey(Skill skill)
        {
            return string.Format("{0}", skill.refInfo.id);
        }

        /// <summary>
        ///     获取冷却时间
        /// </summary>
        /// <param name="skill"></param>
        /// <returns></returns>
        public float GetWaitTime(Skill skill)
        {
            float t = 0;
            if (waitTimeMap.ContainsKey(skill)) t = waitTimeMap[skill] - Time.time;
            return t < 0 ? 0 : t;
        }

        /// <summary>
        ///     设置冷却时间
        /// </summary>
        /// <param name="skill"></param>
        private void SetWaitTime(Skill skill)
        {
            if (!waitTimeMap.ContainsKey(skill)) waitTimeMap.Add(skill, 0);
            waitTimeMap[skill] = _self.GetCoolTime(skill);
        }

        public bool IsInCastRange(Vector3 position)
        {
            return Vector3.Distance(_self.GetPosition(), position) < _self.GetCastRadius();
        }

        public bool IsCasting(Skill skill)
        {
            if (!ingMap.ContainsKey(skill))
                return false;
            return ingMap[skill];
        }

        private void StartCast(Skill skill)
        {
            if (!ingMap.ContainsKey(skill))
                ingMap.Add(skill, true);
            else
                ingMap[skill] = true;
        }

        private void EndCast(Skill skill)
        {
            if (!ingMap.ContainsKey(skill))
                return;
            ingMap[skill] = false;
        }

        /// <summary>
        ///     冷却检查
        /// </summary>
        /// <param name="skill"></param>
        /// <returns></returns>
        public bool TimeCheck(Skill skill)
        {
            if (GetWaitTime(skill) > 0) return false;
            return true;
        }

        /// <summary>
        ///     power检查
        /// </summary>
        /// <param name="skill"></param>
        /// <param name="s"></param>
        /// <returns></returns>
        public bool PowerCheck(Skill skill, int time)
        {
            return _self.OnInspect(skill, time);
        }

        /// <summary>
        ///     技能触发
        /// </summary>
        /// <param name="baseEngine"></param>
        private void Cast(BaseEngine baseEngine, int time)
        {
            if (!_self.IsExist())
            {
                Debug.Log("skill cancel obj die");
                if (_notice != null)
                    _notice.Cancel(baseEngine.skill);
                return;
            }

            if (!skillMap.ContainsKey(baseEngine.skill))
                skillMap.Add(baseEngine.skill, baseEngine);
            else
                skillMap[baseEngine.skill] = baseEngine;
            baseEngine.Ignition(this);
        }

        public interface Notice
        {
            void Start(Skill skill);
            void Ready(Skill skill);
            void Stop(Skill skill);
            void End(Skill skill);
            void Cancel(Skill skill);
        }
    }
}